home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / Prog / B-C / C++Source Code Fmtr Folder / Src / CScanner.cp < prev    next >
Encoding:
Text File  |  1991-08-20  |  24.2 KB  |  1,070 lines  |  [TEXT/MPS ]

  1. /*
  2. ** CScanner.cp
  3. */
  4.  
  5. #ifndef __CSCANNER__
  6. #include "CScanner.h"
  7. #endif
  8.  
  9. #ifndef __FORMATTING__
  10. #include "Formatting.h"
  11. #endif
  12.  
  13. #ifndef __CTYPE__
  14. #include <ctype.h>
  15. #endif
  16.  
  17. #ifndef __STDIO__
  18. #include <stdio.h>
  19. #endif
  20.  
  21. #ifndef __STRING__
  22. #include <string.h>
  23. #endif
  24.  
  25.  
  26.  
  27.  
  28. /*
  29. ** TextPtr encapsulates the pointer into the text buffer
  30. */
  31. typedef const unsigned char* TextPtr;
  32.  
  33.  
  34.  
  35. /*
  36. ** class SyntacticLex
  37. **    This class overrides SaveCopy by returning itself.  All Syntactic objects
  38. ** returned by the CScanner are constant and are never modified by any
  39. ** inherited methods.  If this fails to be true, override this method in the
  40. ** derived class
  41. */
  42.  
  43. #pragma segment CScanner
  44. class SyntacticLex : public Syntactic {
  45. public:
  46.     SyntacticLex(int aType, int aMinorType = 0)
  47.         : Syntactic(aType, aMinorType)
  48.         {
  49.         }
  50.  
  51.  
  52.     virtual Boolean IsSeparator() const;
  53.     /*
  54.     ** Return true.
  55.     */
  56.  
  57.     virtual const Syntactic *SaveCopy() const;
  58.     /*
  59.     ** The object cannot be modified by any of its methods, so
  60.     ** return itself.
  61.     */
  62.  
  63.     virtual Boolean Display(Formatting *aFormat) = 0;
  64.     /*
  65.     ** Format and display this syntactic item.  The format to use when
  66.     ** displaying is passed in as its only argument.
  67.     */
  68. };
  69.  
  70.  
  71. Boolean SyntacticLex::IsSeparator() const
  72. {
  73.     return (false);
  74. }
  75.  
  76.  
  77. const Syntactic *SyntacticLex::SaveCopy() const
  78. {
  79.     return (this);
  80. }
  81.  
  82.  
  83.  
  84.  
  85. /*
  86. ** class LexicalToken
  87. **    This class represents tokens whose body is in the CScanner buffer itself
  88. */
  89.  
  90. #pragma segment CScanner
  91. class LexicalToken : public SyntacticLex {
  92. public:
  93.     LexicalToken(short aType, TextPtr start)
  94.         : SyntacticLex(aType),
  95.           fStart(start),
  96.           fEnd(start + strlen((char *)start))
  97.         {
  98.         }
  99.  
  100.     LexicalToken(short aType, short aMinorType, TextPtr start)
  101.         : SyntacticLex(aType, aMinorType),
  102.           fStart(start),
  103.           fEnd(start + strlen((char *)start))
  104.         {
  105.         }
  106.  
  107.     LexicalToken(short aType, TextPtr start, TextPtr end)
  108.         : SyntacticLex(aType),
  109.           fStart(start),
  110.           fEnd(end)
  111.         {
  112.         }
  113.  
  114.     LexicalToken(short aType, short aMinorType, TextPtr start, TextPtr end)
  115.         : SyntacticLex(aType, aMinorType),
  116.           fStart(start),
  117.           fEnd(end)
  118.         {
  119.         }
  120.  
  121.     virtual Boolean Display(Formatting *aFormat);
  122.     /*
  123.     ** Display the token using the formatting information given by
  124.     ** aFormat
  125.     */
  126.  
  127. protected:
  128.     TextPtr fStart;
  129.     TextPtr fEnd;
  130. };
  131.  
  132.  
  133. Boolean LexicalToken::Display(Formatting *aFormat)
  134. {
  135.     aFormat->Write((char *)fStart, fEnd - fStart);
  136.     return (fEnd != fStart);
  137. }
  138.  
  139.  
  140.  
  141. /*
  142. ** class PredefinedToken
  143. **    Predefined tokens which never change their form
  144. */
  145.  
  146. #pragma segment CScanner
  147. class PredefinedToken : public LexicalToken {
  148. public:
  149.     PredefinedToken(short aType, const char *aString)
  150.         : LexicalToken(aType, (TextPtr)aString)
  151.         {
  152.         }
  153.  
  154.     PredefinedToken(short aType, const char *aString, short aMinorType)
  155.         : LexicalToken(aType, aMinorType, (TextPtr)aString)
  156.         {
  157.         }
  158.  
  159.     PredefinedToken(const char *aString, short aType)
  160.         : LexicalToken(aType, (TextPtr)aString)
  161.         {
  162.         }
  163.  
  164.     PredefinedToken(const char *aString, short aType, short aMinorType)
  165.         : LexicalToken(aType, aMinorType, (TextPtr)aString)
  166.         {
  167.         }
  168.  
  169.     const char *String() const
  170.     {
  171.         return ((const char *)fStart);
  172.     }
  173.     // Return the string in the token itself
  174.  
  175. };
  176.  
  177.  
  178.  
  179.  
  180. /*
  181. ** class ReservedWord:
  182. **    Reserved words are a subclass of PredefinedTokens.
  183. */
  184.  
  185. #pragma segment CScanner
  186. class ReservedWord : public PredefinedToken {
  187. public:
  188.     ReservedWord(short aType, const char *aString)
  189.         : PredefinedToken(aType, aString)
  190.         {
  191.         }
  192.  
  193.     ReservedWord(short aType, const char *aString, short aMinorType)
  194.         : PredefinedToken(aType, aString, aMinorType)
  195.         {
  196.         }
  197.  
  198.     ReservedWord(const char *aString, short aType)
  199.         : PredefinedToken(aType, aString)
  200.         {
  201.         }
  202.  
  203.     ReservedWord(const char *aString, short aType, short aMinorType)
  204.         : PredefinedToken(aType, aString, aMinorType)
  205.         {
  206.         }
  207. };
  208.  
  209.  
  210.  
  211.  
  212. /*
  213. ** class OperatorToken:
  214. **    Operators are a subclass of PredefinedTokens.
  215. */
  216.  
  217. #pragma segment CScanner
  218. class OperatorToken : public PredefinedToken {
  219. public:
  220.     OperatorToken(short aType, const char *aString)
  221.         : PredefinedToken(aType, aString)
  222.         {
  223.         }
  224.  
  225.     OperatorToken(short aType, const char *aString, short aMinorType)
  226.         : PredefinedToken(aType, aString, aMinorType)
  227.         {
  228.         }
  229.  
  230.     OperatorToken(const char *aString, short aType)
  231.         : PredefinedToken(aType, aString)
  232.         {
  233.         }
  234.  
  235.     OperatorToken(const char *aString, short aType, short aMinorType)
  236.         : PredefinedToken(aType, aString, aMinorType)
  237.         {
  238.         }
  239. };
  240.  
  241.  
  242.  
  243.  
  244. /*
  245. ** class WhiteSpaceToken:
  246. **    This class encapsulates those items which are white space (non-separators)
  247. ** but which the formatter is concerned about.  These include comments, newlines
  248. ** preprocessor lines, etc.
  249. */
  250.  
  251. #pragma segment CScanner
  252. class WhiteSpaceToken : public SyntacticLex {
  253. public:
  254.     WhiteSpaceToken(int aType)
  255.         : SyntacticLex(aType)
  256.         {
  257.         }
  258.  
  259.  
  260.     virtual Boolean IsSeparator() const;
  261.     /*
  262.     ** Return false.
  263.     */
  264.  
  265.     virtual Boolean Display(Formatting *aFormat) = 0;
  266.     /*
  267.     ** Format and display this syntactic item.  The format to use when
  268.     ** displaying is passed in as its only argument.
  269.     */
  270. };
  271.  
  272.  
  273. Boolean WhiteSpaceToken::IsSeparator() const
  274. {
  275.     return (true);
  276. }
  277.  
  278.  
  279.  
  280. /*
  281. ** class NewLineToken
  282. **    This class encapsulates source new lines.  Source new lines may or may
  283. ** not be passed through to the output depending on what the user has said
  284. ** about new line preservation
  285. */
  286.  
  287. #pragma segment CScanner
  288. class NewLineToken : public WhiteSpaceToken {
  289. public:
  290.     NewLineToken(short aType)
  291.         : WhiteSpaceToken(aType)
  292.         {
  293.         }
  294.  
  295.     virtual Boolean Display(Formatting *aFormat);
  296. };
  297.  
  298.  
  299. Boolean NewLineToken::Display(Formatting *aFormat)
  300. {
  301.     if (Type() == kSLex_Null)
  302.         aFormat->Putc('\\');
  303.     aFormat->NewLine();
  304.  
  305.     return (true);
  306. }
  307.  
  308.  
  309.  
  310. /*
  311. ** class CommentToken
  312. **    This class contains comments.  Comments are separators and are formatted
  313. ** by the Formatting class.  For implementation purposes, "# preprocessor"
  314. ** lines are also considered comments.
  315. */
  316.  
  317. #pragma segment CScanner
  318. class CommentToken : public WhiteSpaceToken {
  319. public:
  320.     CommentToken(short aType, TextPtr start, TextPtr end)
  321.         : WhiteSpaceToken(aType),
  322.           fStart(start),
  323.           fEnd(end)
  324.         {
  325.         // Check if this is a formatting type of comment.  If it is, set the
  326.         // minor type to the requisite type of change.  A "formatting off"
  327.         // comment is of the form "//ƒ-" or "/*ƒ-", while a "formatting on"
  328.         // comment is of the form "//ƒ+" or "/*ƒ+"
  329.         if (start[2] == (unsigned char)'ƒ')
  330.             if (start[3] == '+')
  331.                 MinorSexChange(kSLex_CommentFormatOn);
  332.             else if (start[3] == '-')
  333.                 MinorSexChange(kSLex_CommentFormatOff);
  334.         }
  335.  
  336.     virtual Boolean Display(Formatting *aFormat);
  337.     /*
  338.     ** Make the Formatting* do the work
  339.     */
  340.  
  341. private:
  342.     TextPtr fStart;
  343.     TextPtr fEnd;
  344. };
  345.  
  346.  
  347. Boolean CommentToken::Display(Formatting *aFormat)
  348. {
  349.     aFormat->ExecuteGlue((FormatString)"s#");
  350.     switch (Type()) {
  351.     case kSLex_Comment:
  352.         aFormat->Comment((const char *)fStart, (const char *)fEnd);
  353.         break;
  354.  
  355.     case kSLex_PoundLine:
  356.         /*
  357.         ** Assure that the preprocessor line starts on a fresh line
  358.         */
  359.         aFormat->ExecuteGlue((FormatString)"&n");
  360.         // !!! FALL THROUGH !!!
  361.  
  362.     default:
  363.         aFormat->Write((char *)fStart, fEnd - fStart);
  364.         break;
  365.     }
  366.  
  367.     return (fStart != fEnd);
  368. }
  369.  
  370.  
  371.  
  372.  
  373. /*ƒ-
  374. ** Predefined token types
  375. */
  376. static unsigned char   gErrText[] = "——— Error ———";
  377. static LexicalToken    gErr            (kSErr,                gErrText, gErrText+13);
  378. static NewLineToken       gNewLine        (kSLex_NewLine);
  379. static NewLineToken       gEOF            (kSLex_EOF);
  380. static NewLineToken       gContinuation(kSLex_Null);
  381.  
  382. static PredefinedToken gClassColon    (kSLex_ClassColon,    "::");
  383. static PredefinedToken gColon        (kSLex_Colon,        ":");
  384. static PredefinedToken gSemiColon    (kSLex_SemiColon,    ";");
  385. static PredefinedToken gLParen        (kSLex_LParen,        "(");
  386. static PredefinedToken gRParen        (kSLex_RParen,        ")");
  387. static PredefinedToken gLBrace        (kSLex_LBrace,        "[");
  388. static PredefinedToken gRBrace        (kSLex_RBrace,        "]");
  389. static PredefinedToken gLCurly        (kSLex_LCurly,        "{");
  390. static PredefinedToken gRCurly        (kSLex_RCurly,        "}");
  391. static PredefinedToken gComma        (kSLex_Comma,        ",");
  392. static PredefinedToken gEllipsis    (kSLex_Ellipsis,    "...");
  393.  
  394. /*
  395. ** Define the operators
  396. */
  397. static OperatorToken gClassStar        (kSLex_Op,            "::*",    kSLex_OpClassStar);
  398. static OperatorToken gQuestion        (kSLex_Op,            "?",    kSLex_OpQuestion);
  399. static OperatorToken gPeriod        (kSLex_Op,            ".",    kSLex_OpDot);
  400. static OperatorToken gPeriodStar    (kSLex_Op,            ".*",    kSLex_OpDotStar);
  401. static OperatorToken gAdd            (kSLex_Op,            "+",    kSLex_OpAdd);
  402. static OperatorToken gSub            (kSLex_Op,            "-",    kSLex_OpSub);
  403. static OperatorToken gMul            (kSLex_Op,            "*",    kSLex_OpMul);
  404. static OperatorToken gDiv            (kSLex_Op,            "/",    kSLex_OpDiv);
  405. static OperatorToken gMod            (kSLex_Op,            "%",    kSLex_OpMod);
  406. static OperatorToken gXor            (kSLex_Op,            "^",    kSLex_OpBXor);
  407. static OperatorToken gLNot            (kSLex_Op,            "!",    kSLex_OpLNot);
  408. static OperatorToken gLAnd            (kSLex_Op,            "&&",    kSLex_OpLAnd);
  409. static OperatorToken gLOr            (kSLex_Op,            "||",    kSLex_OpLOr);
  410. static OperatorToken gBNot            (kSLex_Op,            "~",    kSLex_OpBNot);
  411. static OperatorToken gBAnd            (kSLex_Op,            "&",    kSLex_OpBAnd);
  412. static OperatorToken gBOr            (kSLex_Op,            "|",    kSLex_OpBOr);
  413. static OperatorToken gLSH            (kSLex_Op,            "<<",    kSLex_OpLSh);
  414. static OperatorToken gRSH            (kSLex_Op,            ">>",    kSLex_OpRSh);
  415. static OperatorToken gAssign        (kSLex_Op,            "=",    kSLex_OpAssign);
  416. static OperatorToken gLT            (kSLex_Op,            "<",    kSLex_OpLT);
  417. static OperatorToken gLE            (kSLex_Op,            "<=",    kSLex_OpLE);
  418. static OperatorToken gEQ            (kSLex_Op,            "==",    kSLex_OpEQ);
  419. static OperatorToken gNE            (kSLex_Op,            "!=",    kSLex_OpNE);
  420. static OperatorToken gGE            (kSLex_Op,            ">=",    kSLex_OpGE);
  421. static OperatorToken gGT            (kSLex_Op,            ">",    kSLex_OpGT);
  422. static OperatorToken gAddAssign        (kSLex_Op,            "+=",    kSLex_OpAssignAdd);
  423. static OperatorToken gSubAssign        (kSLex_Op,            "-=",    kSLex_OpAssignSub);
  424. static OperatorToken gMulAssign        (kSLex_Op,            "*=",    kSLex_OpAssignMul);
  425. static OperatorToken gDivAssign        (kSLex_Op,            "/=",    kSLex_OpAssignDiv);
  426. static OperatorToken gModAssign        (kSLex_Op,            "%=",    kSLex_OpAssignMod);
  427. static OperatorToken gXorAssign        (kSLex_Op,            "^=",    kSLex_OpAssignBXor);
  428. static OperatorToken gBAndAssign    (kSLex_Op,            "&=",    kSLex_OpAssignBAnd);
  429. static OperatorToken gBOrAssign        (kSLex_Op,            "|=",    kSLex_OpAssignBOr);
  430. static OperatorToken gLSHAssign        (kSLex_Op,            "<<=",    kSLex_OpAssignLSh);
  431. static OperatorToken gRSHAssign        (kSLex_Op,            ">>=",    kSLex_OpAssignRSh);
  432. static OperatorToken gDecr            (kSLex_Op,            "--",    kSLex_OpMinusMinus);
  433. static OperatorToken gIncr            (kSLex_Op,            "++",    kSLex_OpPlusPlus);
  434. static OperatorToken gPointer        (kSLex_Op,            "->",    kSLex_OpPointer);
  435. static OperatorToken gPointerStar    (kSLex_Op,            "->*",    kSLex_OpPointerStar);
  436.  
  437.  
  438.  
  439. /*ƒ-
  440. ** Reserved words
  441. */
  442. static ReservedWord gAuto        ("auto",        kSLex_Decl);
  443. static ReservedWord gBreak        ("break",        kSLex_Break,    kSLex_BreakBreak);
  444. static ReservedWord gCase        ("case",        kSLex_Case);
  445. static ReservedWord gChar        ("char",        kSLex_Decl);
  446. static ReservedWord gClass        ("class",        kSLex_Struct,    kSLex_StructClass);
  447. static ReservedWord gConst        ("const",        kSLex_Decl,        kSLex_DeclConst);
  448. static ReservedWord gContinue    ("continue",    kSLex_Break,    kSLex_BreakContinue);
  449. static ReservedWord gDefault    ("default",        kSLex_Default);
  450. static ReservedWord gDo            ("do",            kSLex_Do);
  451. static ReservedWord gDouble        ("double",        kSLex_Decl);
  452. static ReservedWord gElse        ("else",        kSLex_Else);
  453. static ReservedWord gEnum        ("enum",        kSLex_Struct,    kSLex_StructEnum);
  454. static ReservedWord gExtended    ("extended",    kSLex_Decl);
  455. static ReservedWord gExtern        ("extern",        kSLex_Decl);
  456. static ReservedWord gFor        ("for",            kSLex_For);
  457. static ReservedWord gFloat        ("float",        kSLex_Decl);
  458. static ReservedWord gFriend        ("friend",        kSLex_Decl);
  459. static ReservedWord gGoto        ("goto",        kSLex_Break,    kSLex_BreakGoto);
  460. static ReservedWord gIf            ("if",            kSLex_If);
  461. static ReservedWord gInline        ("inline",        kSLex_Decl);
  462. static ReservedWord gInt        ("int",            kSLex_Decl);
  463. static ReservedWord gLong        ("long",        kSLex_Decl);
  464. static ReservedWord gOperator    ("operator",    kSLex_DeclOperator,    kSLex_DeclOperator);
  465. static ReservedWord gPascal        ("pascal",        kSLex_Decl);
  466. static ReservedWord gPrivate    ("private",        kSLex_Public,    kSLex_PublicPrivate);
  467. static ReservedWord gProtected    ("protected",    kSLex_Public,    kSLex_PublicProtected);
  468. static ReservedWord gPublic        ("public",        kSLex_Public,    kSLex_PublicPublic);
  469. static ReservedWord gRegister    ("register",    kSLex_Decl);
  470. static ReservedWord gReturn        ("return",        kSLex_Break,    kSLex_BreakReturn);
  471. static ReservedWord gShort        ("short",        kSLex_Decl);
  472. static ReservedWord gSigned        ("signed",        kSLex_Decl);
  473. static ReservedWord gStatic        ("static",        kSLex_Decl);
  474. static ReservedWord gStruct        ("struct",        kSLex_Struct,    kSLex_StructStruct);
  475. static ReservedWord gSwitch        ("switch",        kSLex_Switch);
  476. static ReservedWord gTemplate    ("template",    kSLex_Struct,    kSLex_StructTemplate);
  477. static ReservedWord gTypedef    ("typedef",        kSLex_Decl);
  478. static ReservedWord gUnion        ("union",        kSLex_Struct,    kSLex_StructUnion);
  479. static ReservedWord gUnsigned    ("unsigned",    kSLex_Decl);
  480. static ReservedWord gVa_dcl        ("va_dcl",        kSLex_Decl);
  481. static ReservedWord gVirtual    ("virtual",        kSLex_Decl);
  482. static ReservedWord gVoid        ("void",        kSLex_Decl);
  483. static ReservedWord gVolatile    ("volatile",    kSLex_Decl,        kSLex_DeclVolatile);
  484. static ReservedWord gWhile        ("while",        kSLex_While);
  485. //ƒ+
  486.  
  487.  
  488. #pragma segment CScanner
  489. static Syntactic *lookup(TextPtr start, TextPtr end)
  490. {
  491.     int len = end - start;
  492.  
  493.     // These are the bounds on the lengths of the words in the reservedWordList
  494.     const int kMinWordLength = 2;
  495.     const int kMaxWordLength = 9;
  496.  
  497.     //ƒ- Note that this list is in alphabetical order
  498.     static ReservedWord* reservedWordList[] = {
  499.           &gAuto    
  500.         , &gBreak
  501.         , &gCase
  502.         , &gChar
  503.         , &gClass
  504.         , &gConst
  505.         , &gContinue
  506.         , &gDefault
  507.         , &gDo
  508.         , &gDouble
  509.         , &gElse
  510.         , &gEnum
  511.         , &gExtended
  512.         , &gExtern
  513.         , &gFloat
  514.         , &gFor
  515.         , &gFriend
  516.         , &gGoto
  517.         , &gIf
  518.         , &gInline
  519.         , &gInt
  520.         , &gLong
  521.         , &gOperator
  522.         , &gPascal
  523.         , &gPrivate
  524.         , &gProtected
  525.         , &gPublic
  526.         , &gRegister
  527.         , &gReturn
  528.         , &gShort
  529.         , &gSigned
  530.         , &gStatic
  531.         , &gStruct
  532.         , &gSwitch
  533.         , &gTemplate
  534.         , &gTypedef
  535.         , &gUnion
  536.         , &gUnsigned
  537.         , &gVa_dcl
  538.         , &gVirtual
  539.         , &gVoid
  540.         , &gVolatile
  541.         , &gWhile
  542.         };
  543.  
  544.     //ƒ+
  545.     if (len <= kMaxWordLength && len >= kMinWordLength) {
  546.         int min = 0;
  547.         int max = sizeof(reservedWordList) / sizeof(reservedWordList[0]) - 1;
  548.  
  549.         while (min <= max) {
  550.             int mid = (min + max) / 2;
  551.             int cmp = strncmp((char *)start, reservedWordList[mid]->String(), len);
  552.  
  553.             // fprintf(stderr, "reservedWordList[%2d] = %s\n", mid, reservedWordList[mid]->String());
  554.             if (cmp == 0)
  555.                 cmp = strlen(reservedWordList[mid]->String()) - len;
  556.  
  557.             if (cmp == 0)
  558.                 return (reservedWordList[mid]);
  559.             else if (cmp < 0)
  560.                 max = mid - 1;
  561.             else
  562.                 min = mid + 1;
  563.         }
  564.     }
  565.  
  566.     return (new LexicalToken(kSLex_Id, start, end));
  567. }
  568.  
  569.  
  570. /*
  571. ** w1
  572. ** Return a token corresponding to the single character passed it.  The assign
  573. ** arg is true if this is the special case of op=
  574. */
  575. #pragma segment CScanner
  576. static Syntactic *w1(int aChar, Boolean assign = false)
  577. {
  578.     //ƒ-
  579.     switch (aChar) {
  580.     case '+':    return (assign ? &gAddAssign  :&gAdd);
  581.     case '-':    return (assign ? &gSubAssign  :&gSub);
  582.     case '*':    return (assign ? &gMulAssign  :&gMul);
  583.     case '/':    return (assign ? &gDivAssign  :&gDiv);
  584.     case '%':    return (assign ? &gModAssign  :&gMod);
  585.     case '^':    return (assign ? &gXorAssign  :&gXor);
  586.     case '!':    return (assign ? &gNE          :&gLNot);
  587.     case '&':    return (assign ? &gBAndAssign :&gBAnd);
  588.     case '|':    return (assign ? &gBOrAssign  :&gBOr);
  589.     case '=':    return (assign ? &gEQ          :&gAssign);
  590.     case '<':    return (assign ? &gLE          :&gLT);
  591.     case '>':    return (assign ? &gGE          :&gGT);
  592.     case '~':    return (&gBNot);
  593.     case ':':    return (&gColon);
  594.     case '.':    return (&gPeriod);
  595.     case '?':    return (&gQuestion);
  596.     case '\0':    return (&gEOF);
  597.     default:    return (&gErr);
  598.     }
  599.     //ƒ+
  600. }
  601.  
  602.  
  603.  
  604. // w2
  605. #pragma segment CScanner
  606. static Syntactic *w2(int aChar)
  607. {
  608.     //ƒ-
  609.     switch (aChar) {
  610.     case '+':    return (&gIncr);
  611.     case '-':    return (&gDecr);
  612.     case '&':    return (&gLAnd);
  613.     case '|':    return (&gLOr);
  614.     case '<':    return (&gLSH);
  615.     case '>':    return (&gRSH);
  616.     case '=':    return (&gEQ);
  617.     case ':':    return (&gClassColon);
  618.     default:    return (&gErr);
  619.     }
  620.     //ƒ+
  621. }
  622.  
  623.  
  624.  
  625.  
  626.  
  627. #pragma segment CScanner
  628. inline Boolean isodigit(int ch)
  629. {
  630.     return ((ch >= '0') && (ch <= '7'));
  631. }
  632.  
  633.  
  634.  
  635. // CScanner::ICScanner
  636. #pragma segment CScanner
  637. short CScanner::ICScanner()
  638. {
  639.     return (noErr);
  640. }
  641.  
  642.  
  643. // CScanner::NextToken
  644. #pragma segment CScanner
  645. Syntactic *CScanner::NextToken()
  646. {
  647.     int ch;
  648.     int firstCh;
  649.     TextPtr start;
  650.  
  651.     // End of file check
  652.     if (fBuffer >= fBufferEnd) {
  653.         return (&gEOF);
  654.     }
  655.  
  656.     // Skip over the white space (0xCA is the non-breaking space)
  657.     while (fBuffer < fBufferEnd) {
  658.         ch = NextChar();
  659.         if (ch == '\n') {
  660.             fLastTokenStart = fBuffer - 1;
  661.             return (&gNewLine);
  662.         }
  663.         if (!isspace(ch))
  664.             if (ch != (unsigned char)0xCA)
  665.                 break;
  666.     }
  667.  
  668.     // End of file check
  669.     if (fBuffer == fBufferEnd && isspace(ch)) {
  670.         return (&gEOF);
  671.     }
  672.  
  673.     // Remember the start of the token
  674.     fLastTokenStart = start = fBuffer - 1;
  675.  
  676.     // Identifier scanning
  677.     if (isalpha(ch) || (ch == '_')) {
  678.         while (isalnum(ch) || (ch == '_') || (ch == '%') || (ch == '$'))
  679.             ch = NextChar();
  680.  
  681.         if (!IsEOF())
  682.             PushBack();
  683.         return (lookup(start, fBuffer));
  684.     }
  685.  
  686.     // Number scanning
  687.     if (isdigit(ch)) {
  688.         if (ch == '0') {
  689.             ch = NextChar();
  690.             if (ch == 'x' || ch == 'X') {
  691.                 ch = NextChar();
  692.                 while (isxdigit(ch))
  693.                     ch = NextChar();
  694.  
  695.                 goto numDone;
  696.             }
  697.         }
  698.  
  699.         while (isdigit(ch))
  700.             ch = NextChar();
  701.  
  702.         if (ch == '.') {
  703.             ch = NextChar();
  704.             if (ch == '.') {
  705.                 PushBack();                        // '..' range symbol pushed back
  706.                 PushBack();
  707.                 goto numDone;
  708.             }
  709.  
  710.             while (isdigit(ch))
  711.                 ch = NextChar();
  712.  
  713.             if (ch == 'e' || ch == 'E') {
  714.                 ch = NextChar();
  715.                 if (ch == '+' || ch == '-')
  716.                     ch = NextChar();
  717.  
  718.                 if (!isdigit(ch))
  719.                     goto numDone;
  720.  
  721.                 while (isdigit(ch))
  722.                     ch = NextChar();
  723.             }
  724.         }
  725.  
  726. numDone:;
  727.         // Allow the optional type marker.
  728.         if (ch == 'l' || ch == 'L')
  729.             ch = NextChar();
  730.  
  731.         if (!IsEOF())
  732.             PushBack();
  733.         return (new LexicalToken(kSLex_Value, start, fBuffer));
  734.     }
  735.  
  736.     firstCh = ch;
  737.     switch (firstCh) {
  738.         //ƒ-
  739.     case ';':    return (&gSemiColon);
  740.     case '(':    return (&gLParen);
  741.     case ')':    return (&gRParen);
  742.     case '{':    return (&gLCurly);
  743.     case '}':    return (&gRCurly);
  744.     case '[':    return (&gLBrace);
  745.     case ']':    return (&gRBrace);
  746.     case ',':    return (&gComma);
  747.     case '?':    return (&gQuestion);
  748.     //ƒ+
  749.  
  750.     case '"':
  751.     case '\'':
  752.         for (;;) {
  753.             ch = NextChar();
  754.             if (ch == '\\')
  755.                 NextChar();
  756.             else if (IsEOF() || (ch == firstCh))
  757.                 break;
  758.         }
  759.  
  760.         return (new LexicalToken(kSLex_Value, start, fBuffer));
  761.         //break;
  762.  
  763.     case ':':
  764.         ch = NextChar();
  765.         if (ch == ':') {
  766.             if (NextChar() == '*')
  767.                 return (&gClassStar);
  768.             PushBack();
  769.             return (&gClassColon);
  770.         } else {
  771.             PushBack();
  772.             return (&gColon);
  773.         }
  774.         break;
  775.  
  776.     case '*':
  777.     case '%':
  778.     case '^':
  779.     case '!':
  780.         ch = NextChar();
  781.         if (ch == '=')
  782.             return (w1(firstCh, true));
  783.         else {
  784.             PushBack();
  785.             return (w1(firstCh));
  786.         }
  787.         break;
  788.  
  789.     case '+':
  790.     case '&':
  791.     case '|':
  792.     case '=':
  793.         ch = NextChar();
  794.         if (ch == '=')
  795.             return (w1(firstCh, true));
  796.         else if (ch == firstCh)
  797.             return (w2(firstCh));
  798.         else {
  799.             PushBack();
  800.             return (w1(firstCh));
  801.         }
  802.         break;
  803.  
  804.     case '-':
  805.         ch = NextChar();
  806.         if (ch == '=')
  807.             return (&gSubAssign);
  808.         else if (ch == '-')
  809.             return (&gDecr);
  810.         else if (ch == '>') {
  811.             ch = NextChar();
  812.             if (ch == '*')
  813.                 return (&gPointerStar);
  814.             else {
  815.                 PushBack();
  816.                 return (&gPointer);
  817.             }
  818.         } else {
  819.             PushBack();
  820.             return (&gSub);
  821.         }
  822.         break;
  823.  
  824.     case '<':
  825.     case '>':
  826.         ch = NextChar();
  827.         if (ch == '=')
  828.             return (w1(firstCh, true));
  829.         else if (ch == firstCh) {
  830.             ch = NextChar();
  831.             if (ch == '=')
  832.                 return ((firstCh == '<') ? &gLSHAssign : &gRSHAssign);
  833.             else {
  834.                 PushBack();
  835.                 return (w2(firstCh));
  836.             }
  837.         } else {
  838.             PushBack();
  839.             return (w1(firstCh));
  840.         }
  841.         break;
  842.  
  843.     case '/':
  844.         ch = NextChar();
  845.         switch (ch) {
  846.         case '/':
  847.             // Copy the comment up to but not including the '\n'
  848.             do
  849.                 ch = NextChar();
  850.             while ((ch > 0) && (ch != '\n'));
  851.             if (ch == '\n')
  852.                 PushBack();
  853.             return (new CommentToken(kSLex_Comment, start, fBuffer));
  854.             //break;
  855.  
  856.         case '*':
  857.             while (ch > 0) {
  858.                 ch = NextChar();
  859.                 if (ch == '*') {
  860.                     while (ch == '*')
  861.                         ch = NextChar();
  862.                     if (ch == '/')
  863.                         break;
  864.                 }
  865.             }
  866.             return (new CommentToken(kSLex_Comment, start, fBuffer));
  867.             //break;
  868.  
  869.         case '=':
  870.             return (&gDivAssign);
  871.             //break;
  872.  
  873.         default:
  874.             PushBack();
  875.             return (&gDiv);
  876.         }
  877.         break;
  878.  
  879.     case '.':
  880.         ch = NextChar();
  881.         if (ch == '*')
  882.             return (&gPeriodStar);
  883.         else if (ch == '.') {
  884.             if (NextChar() == '.')
  885.                 return (&gEllipsis);
  886.             PushBack();
  887.         }
  888.  
  889.         PushBack();
  890.         return (&gPeriod);
  891.         // break;
  892.  
  893.     case '#':
  894.         {
  895.             int type = 0;                        // Assume other type
  896.  
  897.             enum {
  898.                 kStart                            // Look for "if", "elif", "else"
  899.                 , kText                            // Normal text
  900.                 , kComment                        // Within a "/*" comment
  901.                 , kCommentToEOL                    // Within a "//" comment
  902.                 , kString                        // Within a string
  903.                 , kChar                            // Within a character constant
  904.             } state = kStart;
  905.  
  906.             /*
  907.             ** State transitions:
  908.             **    kStart        -> kText
  909.             **    kText        -> {kComment, kString, kChar}
  910.             **    kComment    -> kText
  911.             **    kString        -> kText
  912.             **    kChar        -> kText
  913.             **
  914.             **    Note that we don't allow comments immediately following the
  915.             ** "#".  If this is a problem, extend the state machine by adding
  916.             ** kStartComment, which commutes with kStart.  For now, we don't
  917.             ** care.
  918.             **    The only reason we have a state machine here at all is because
  919.             ** there is some code extent which has a multi-line comment at the
  920.             ** end of a #define without line continuation characters at the end.
  921.             */
  922.             for (;;) {
  923.                 // Get the next character.  Strings, character constants and
  924.                 // line continuations use "\" to strop the character following.
  925.                 ch = NextChar();
  926.                 if (ch == '\\') {
  927.                     NextChar();
  928.                     continue;
  929.                 }
  930.  
  931.                 // Blanks don't matter, but newlines do.
  932.                 if (isspace(ch) && ch != '\n')
  933.                     continue;
  934.  
  935.                 // EOF is fatal in all states
  936.                 if (IsEOF()) {
  937.                     Syntactic * aToken = new CommentToken(kSLex_PoundLine, start, fBuffer);
  938.                     aToken->MinorSexChange(type);
  939.                     return (aToken);
  940.                 }
  941.  
  942.                 switch (state) {
  943.                 case kStart:
  944.                     // Determine what type of "#" this is.  Following this is text.
  945.                     state = kText;
  946.  
  947.                     if (strncmp((char *)fBuffer - 1, "if", 2) == 0)
  948.                         type = kSLex_PoundIf;
  949.                     else if (strncmp((char *)fBuffer - 1, "elif", 4) == 0)
  950.                         type = kSLex_PoundElif;
  951.                     else if (strncmp((char *)fBuffer - 1, "else", 4) == 0)
  952.                         type = kSLex_PoundElse;
  953.                     else if (strncmp((char *)fBuffer - 1, "endif", 5) == 0)
  954.                         type = kSLex_PoundEndIf;
  955.                     else
  956.                         // Do this character as text.
  957.                         goto doText;
  958.                     break;
  959.  
  960.                 case kText:
  961.                     // Switch to one of the subordinate states.
  962. doText:                switch (ch) {
  963.                     case '"':
  964.                         state = kString;
  965.                         break;
  966.                     case '\'':
  967.                         state = kChar;
  968.                         break;
  969.  
  970.                     case '/':
  971.                         switch (NextChar()) {
  972.                         case '*':
  973.                             state = kComment;
  974.                             break;
  975.                         case '/':
  976.                             state = kCommentToEOL;
  977.                             break;
  978.                         default:
  979.                             PushBack();
  980.                             break;
  981.                         }
  982.                         break;
  983.  
  984.                     case '\\':
  985.                         // End of line continuation character?  Ignore the character
  986.                         // following as if it is a newline the continuation hides
  987.                         // it and if it isn't a newline it would be incorporated
  988.                         // as part of the token.
  989.                         NextChar();
  990.                         break;
  991.  
  992.                     case '\n':
  993.                         {
  994.                             PushBack();
  995.                             Syntactic * aToken = new CommentToken(kSLex_PoundLine, start, fBuffer);
  996.                             aToken->MinorSexChange(type);
  997.                             return (aToken);
  998.                         }
  999.                     }
  1000.                     break;
  1001.  
  1002.                 case kComment:
  1003.                     // Check if this is the end of the comment
  1004.                     if (ch == '*')
  1005.                         if (NextChar() == '/')
  1006.                             state = kText;
  1007.                         else
  1008.                             PushBack();
  1009.                     break;
  1010.  
  1011.                 case kCommentToEOL:
  1012.                     // Keep going to the end of the comment line.  If there is
  1013.                     // a continuation character, check if the character following
  1014.                     // is a newline.  If it is, the state becomes kText.  If the
  1015.                     // character is a newline, then change state to kText and let
  1016.                     // doText do the work.  Otherwise ignore the character
  1017.                     if (ch == '\\') {
  1018.                         if (NextChar() == '\n')
  1019.                             state = kText;
  1020.                     } else if (ch == '\n') {
  1021.                         state = kText;
  1022.                         goto doText;
  1023.                     }
  1024.                     break;
  1025.  
  1026.                 case kString:
  1027.                     if (ch == '"')
  1028.                         state = kText;
  1029.                     break;
  1030.                 case kChar:
  1031.                     if (ch == '\'')
  1032.                         state = kText;
  1033.                     break;
  1034.                 }
  1035.             }
  1036.         }
  1037.         break;
  1038.  
  1039.     case '\\':
  1040.         ch = NextChar();
  1041.         if (ch == '\n')
  1042.             return (&gContinuation);
  1043.         else {
  1044.             PushBack();
  1045.             return (&gErr);
  1046.         }
  1047.         break;
  1048.  
  1049.     default:
  1050.         return (w1(firstCh));
  1051.     }
  1052. }
  1053.  
  1054.  
  1055. // CScanner::LineNumber
  1056. #pragma segment CScanner
  1057. size_t CScanner::LineNumber() const
  1058. {
  1059.     size_t lineNo = 1;
  1060.     const unsigned char *aPtr = fBufferStart;
  1061.  
  1062.     while (aPtr <= fLastTokenStart)
  1063.         if (*aPtr++ == '\n')
  1064.             lineNo++;
  1065.  
  1066.     return (lineNo);
  1067. }
  1068.  
  1069.  
  1070.